package util;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Stream;

public class ObjectUtil {

    private static final char AT_SIGN = '@';
    public static final Null NULL = new Null();
    public static int checkPositive(int i, String name) {
        if (i <= 0) {
            throw new IllegalArgumentException(name + ": " + i + " (expected: > 0)");
        }
        return i;
    }

    public static <T> T checkNotNull(T arg, String text) {
        if (arg == null) {
            throw new NullPointerException(text);
        }
        return arg;
    }

    public static boolean allNotNull(Object... values) {
        return values != null && Stream.of(values).noneMatch(Objects::isNull);
    }

    public static boolean allNull(Object... values) {
        return !anyNotNull(values);
    }

    public static boolean anyNotNull(Object... values) {
        return firstNonNull(values) != null;
    }

    public static boolean anyNull(Object... values) {
        return !allNotNull(values);
    }

    public static <T> T clone(T obj) {
        if (!(obj instanceof Cloneable)) {
            return null;
        } else {
            Class<? extends Object> objClass = obj.getClass();
            Object result=null;
            if (isArray(obj)) {
                Class<?> componentType = objClass.getComponentType();
                if (componentType.isPrimitive()) {
                    int length = Array.getLength(obj);
                    result = Array.newInstance(componentType, length);

                    while(length-- > 0) {
                        Array.set(result, length, Array.get(obj, length));
                    }
                } else {
                    result = ((Object[])obj).clone();
                }
            } else {
                try {
                    result = objClass.getMethod("clone").invoke(obj);
                } catch (ReflectiveOperationException var5) {
                    //throw new CloneFailedException("Exception cloning Cloneable type " + objClass.getName(), var5);
                }
            }

            return (T) result;
        }
    }

    public static <T> T cloneIfPossible(T obj) {
        T clone = clone(obj);
        return clone == null ? obj : clone;
    }

    public static <T extends Comparable<? super T>> int compare(T c1, T c2) {
        return compare(c1, c2, false);
    }

    public static <T extends Comparable<? super T>> int compare(T c1, T c2, boolean nullGreater) {
        if (c1 == c2) {
            return 0;
        } else if (c1 == null) {
            return nullGreater ? 1 : -1;
        } else if (c2 == null) {
            return nullGreater ? -1 : 1;
        } else {
            return c1.compareTo(c2);
        }
    }

    public static boolean CONST(boolean v) {
        return v;
    }

    public static byte CONST(byte v) {
        return v;
    }

    public static char CONST(char v) {
        return v;
    }

    public static double CONST(double v) {
        return v;
    }

    public static float CONST(float v) {
        return v;
    }

    public static int CONST(int v) {
        return v;
    }

    public static long CONST(long v) {
        return v;
    }

    public static short CONST(short v) {
        return v;
    }

    public static <T> T CONST(T v) {
        return v;
    }

    public static byte CONST_BYTE(int v) {
        if (v >= -128 && v <= 127) {
            return (byte)v;
        } else {
            throw new IllegalArgumentException("Supplied value must be a valid byte literal between -128 and 127: [" + v + "]");
        }
    }

    public static short CONST_SHORT(int v) {
        if (v >= -32768 && v <= 32767) {
            return (short)v;
        } else {
            throw new IllegalArgumentException("Supplied value must be a valid byte literal between -32768 and 32767: [" + v + "]");
        }
    }

    public static <T> T defaultIfNull(T object, T defaultValue) {
        return object != null ? object : defaultValue;
    }

    public static boolean equals(Object object1, Object object2) {
        return Objects.equals(object1, object2);
    }

    public static <T> T firstNonNull(T... values) {
        return Stream.of(values).filter(Objects::nonNull).findFirst().orElse((T) null);
    }

    public static <T> Class<T> getClass(T object) {
        return object == null ? null : (Class<T>) object.getClass();
    }

    public static <T> T getFirstNonNull(Supplier<T>... suppliers) {
        return Stream.of(suppliers).filter(Objects::nonNull).map(Supplier::get).filter(Objects::nonNull).findFirst().orElse((T) null);
    }

    public static <T> T getIfNull(T object, Supplier<T> defaultSupplier) {
        return object != null ? object : (T) defaultSupplier;
    }

    public static int hashCode(Object obj) {
        return Objects.hashCode(obj);
    }

    public static String hashCodeHex(Object object) {
        return Integer.toHexString(Objects.hashCode(object));
    }

    public static int hashCodeMulti(Object... objects) {
        int hash = 1;
        if (objects != null) {
            Object[] var2 = objects;
            int var3 = objects.length;

            for(int var4 = 0; var4 < var3; ++var4) {
                Object object = var2[var4];
                int tmpHash = Objects.hashCode(object);
                hash = hash * 31 + tmpHash;
            }
        }

        return hash;
    }

    public static String identityHashCodeHex(Object object) {
        return Integer.toHexString(System.identityHashCode(object));
    }

    public static void identityToString(Appendable appendable, Object object) {
        Objects.requireNonNull(object, "object");
        try {
            appendable.append(object.getClass().getName()).append('@').append(identityHashCodeHex(object));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static String identityToString(Object object) {
        if (object == null) {
            return null;
        } else {
            String name = object.getClass().getName();
            String hexString = identityHashCodeHex(object);
            StringBuilder builder = new StringBuilder(name.length() + 1 + hexString.length());
            builder.append(name).append('@').append(hexString);
            return builder.toString();
        }
    }

    public static void identityToString(StringBuffer buffer, Object object) {
        Objects.requireNonNull(object, "object");
        String name = object.getClass().getName();
        String hexString = identityHashCodeHex(object);
        buffer.ensureCapacity(buffer.length() + name.length() + 1 + hexString.length());
        buffer.append(name).append('@').append(hexString);
    }

    public static void identityToString(StringBuilder builder, Object object) {
        Objects.requireNonNull(object, "object");
        String name = object.getClass().getName();
        String hexString = identityHashCodeHex(object);
        builder.ensureCapacity(builder.length() + name.length() + 1 + hexString.length());
        builder.append(name).append('@').append(hexString);
    }

    public static boolean isArray(Object object) {
        return object != null && object.getClass().isArray();
    }

    public static boolean isEmpty(Object object) {
        if (object == null) {
            return true;
        } else if (object instanceof CharSequence) {
            return ((CharSequence)object).length() == 0;
        } else if (isArray(object)) {
            return Array.getLength(object) == 0;
        } else if (object instanceof Collection) {
            return ((Collection)object).isEmpty();
        } else if (object instanceof Map) {
            return ((Map)object).isEmpty();
        } else if (object instanceof Optional) {
            return !((Optional)object).isPresent();
        } else {
            return false;
        }
    }

    public static boolean isNotEmpty(Object object) {
        return !isEmpty(object);
    }

    @SafeVarargs
    public static <T extends Comparable<? super T>> T max(T... values) {
        T result = null;
        if (values != null) {
            Comparable[] var2 = values;
            int var3 = values.length;

            for(int var4 = 0; var4 < var3; ++var4) {
                T value = (T) var2[var4];
                if (compare(value, result, false) > 0) {
                    result = value;
                }
            }
        }

        return result;
    }

    public static boolean notEqual(Object object1, Object object2) {
        return !Objects.equals(object1, object2);
    }

    public static <T> T requireNonEmpty(T obj) {
        return requireNonEmpty(obj, "object");
    }

    public static <T> T requireNonEmpty(T obj, String message) {
        Objects.requireNonNull(obj, message);
        if (isEmpty(obj)) {
            throw new IllegalArgumentException(message);
        } else {
            return obj;
        }
    }

    public static String toString(Object obj) {
        return Objects.toString(obj, "");
    }

    public static String toString(Object obj, String nullStr) {
        return Objects.toString(obj, nullStr);
    }

    public static class Null implements Serializable {
        private static final long serialVersionUID = 7092611880189329093L;

        Null() {
        }

        private Object readResolve() {
            return ObjectUtil.NULL;
        }
    }
}
